/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun<lingyun@email.com>
 *                  All rights reserved.
 *
 *       Filename:  server.c
 *    Description:  This file is ds18b20 server
 *                 
 *        Version:  1.0.0(25/03/13)
 *         Author:  Huangxiangming <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "25/03/13 17:05:23"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/resource.h>

#include "logger.h"
#include "sqlite_server.h"
#include "server_function.h"
#include "proc.h"

#define MAX_EVENTS			512
#define ARRAY_SIZE(x)		(sizeof(x)/sizeof(x[0]))
#define MSG					"Server has received the data"
#define PIDFILE             "/tmp/.socketd_h_s.pid"

void print_usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);

	printf("-b[daemon] Set program running on background\n");
	printf("-d[debug]: output information to stdout\n");
	printf("-p[port] Socket server port address\n");
	printf("-h[help] Dispaly this help information\n");
	printf("-P[Pack]:Data packing format, with options json and tlv vailable\n");
	printf("-S[Stop]:Stop background progname\n");

	return ;
}

int main(int argc, char **argv)
{
	int						listenfd, connfd;
	int						serv_port = 0;
	int						daemon_flag = 0;
	char					*progname = NULL;
	int						opt;
	int						rv;
	int						i, j;
	int						found;
	char					buf[1024];
	int						k = 0;
	char					*str= NULL;

	data_t					data = {0};
	parse_proc_t             parse_proc = parse_data;      //parse_data_json/tlv

	int             		error;
	socklen_t       		len_error = sizeof(error);
	
	char                    *logfile = "server.log";
	char                    loglevel = LOG_LEVEL_INFO;

	progname = basename(argv[0]);

	//epoll定义部分
	int						epollfd;
	struct epoll_event		event;		//添加fd
	struct epoll_event		event_array[MAX_EVENTS];	//设置可同时处理多少Client
	int						events;		//多少个客户端

	//命令行参数解析部分
	struct option			long_options[] =
	{
		{"background", no_argument, NULL, 'b'},
		{"debug", no_argument, NULL, 'd'},
		{"port", required_argument, NULL, 'p'},
		{"help", no_argument, NULL, 'h'},
		{"Pack", required_argument, NULL, 'P'},
		{"Stop", no_argument, NULL, 'S'},
		{NULL, 0, NULL, 0}
	};

	while( (opt = getopt_long(argc, argv, "bdp:hP:S", long_options, NULL)) != -1)
	{
		switch(opt)
		{
			case 'b':
				daemon_flag = 1;
				break;

			case 'd':
				logfile = "stdout";
				loglevel = LOG_LEVEL_DEBUG;
				break;

			case 'p':
				serv_port = atoi(optarg);
				break;

			case 'h':
				print_usage(progname);
				return EXIT_SUCCESS;

			case 'P':
				if( strcmp(optarg, "json") == 0 )
				{
					parse_proc = parse_data_json;
				}
				else if( strcmp(optarg, "tlv") == 0 )
				{
					parse_proc = parse_data_tlv;
				}
				else
				{
					print_usage(argv[0]);
				}
				break;

			case 'S':
				stop_background_running(PIDFILE);
				return 0;

			default:
				break;
		}
	}

	//检查端口
	if( !serv_port )
	{
		print_usage(progname);
		return -1;
	}

	//日志初始化
	if ( logger_init(logfile, loglevel) < 0)
	{
		fprintf(stderr, "initial logger system failure\n");
		return 1;   //成功返回0，出错返回非零整数
	}
	
	install_signal();

	//设置最大socket数
	set_socket_rlimit();

	//socket服务器初始化
	if ( (listenfd=socket_server_init(NULL, serv_port)) < 0 )
	{
		log_error("%s server listen on port %d failure: %s\n", argv[0], serv_port, strerror(errno));
		return -2;
	}
	log_info("%s server start to listen on port %d\n", argv[0], serv_port);
	
	//建立后台
	if ( !set_background_running(daemon_flag, PIDFILE) )
	{
		goto CleanUp;
	}  

	//epoll部分
	if ( (epollfd=epoll_create(MAX_EVENTS)) < 0 )
	{
		log_error("epoll_create() failure: %s\n", strerror(errno));
		return -3;
	}

	event.events = EPOLLIN;
	event.data.fd = listenfd;

	if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) < 0 )
	{
		log_error("epoll add listen socket failure: %s\n", strerror(errno));
		return -4;
	}

	//sqlite初始化
	if ( sqlite_init() < 0 )
	{
		return 0;
	}

	while( !stop )
	{
		events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1);
		if ( events < 0 )
		{
			log_error("epoll failure: %s\n", strerror(errno));
			break;
		}
		else if ( events == 0 )
		{
			log_warn("epoll get timeout\n");
			continue;
		}
		
		//遍历event_array
		for ( i=0; i<events; i++ )
		{
			if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
			{
				log_warn("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));
				epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}
		
			if ( event_array[i].data.fd == listenfd )
			{
				if ( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0 )
				{
					log_warn("accept new client failure: %s\n", strerror(errno));
					continue;
				}

				event.data.fd = connfd;
				event.events = EPOLLIN;
				if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0 )
				{
					log_warn("epoll add client socket failure: %s\n", strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				log_info("epoll add new client socket[%d] ok\n", connfd);
			}
			//连接成功，进行读写操作
			else	
			{
				memset(buf, 0, sizeof(buf));
				if ( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <= 0 )
				{
					log_warn("socket[%d] read failure or get disconnect.\n", event_array[i].data.fd);
					epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
					close(event_array[i].data.fd);
					continue;
				}
				else//读成功
				{
					log_info("socket[%d] read get %d btyes data.\n", event_array[i].data.fd, rv);
				
					if ( parse_proc((uint8_t *)buf, &data) == 0 )
					{			
						if ( sqlite_table(data.id) == 0 )
						{
							if ( !sqlite_inset(&data) )
							{
								write(event_array[i].data.fd, "server have got data", sizeof("server have got data"));
							}
						}
					}/*parse_proc*/
				}/* read-else */
			}

		}
	}

CleanUp:
	close(listenfd);
	sqlite_close();
	unlink(PIDFILE);
	logger_term();
	return 0;
}


